{"id":"cmvef2pas0007qu2o0mvmeuu6","slug":"pajak kendaraan kalbar","judul":"Pajak Kendaraan Kalbar","deskripsi":"Dua app Android (user + admin) + backend API untuk cek nominal pajak kendaraan: user kirim plat + no WA, admin input 4 nominal, user transfer & konfirmasi via WhatsApp.","parentId":null,"urut":6,"kategori":"mobile","tag":"[\"android\", \"kotlin\", \"nextjs\", \"prisma\", \"postgres\", \"coolify\", \"fcm\"]","status":"aktif","versi":"0.1.0","konsep":"User login Google (Credential Manager) -> input plat (prefix KB hardcoded) + nomor WhatsApp -> backend simpan request PENDING + push FCM ke admin. Admin app: antrian pending -> detail -> input 4 nominal (pajak, biaya cek, biaya admin, biaya kirim) -> kirim; total dihitung backend. User dapat push -> layar rincian + total -> tombol Bayar -> layar rekening (multi-bank, salin no rek) + tombol konfirmasi WhatsApp (wa.me prefilled) + upload bukti transfer -> admin verifikasi -> status PAID/STRUK_SENT + push. Desain: variant V1 Clean Minimal (putih + biru #0a7cff, Linear-style).","createdAt":"2026-10-18T22:48:29.331Z","updatedAt":"2026-10-18T23:45:21.872Z","bagian":[{"id":"cmveh5hp10001n32ofpm9230e","blueprintId":"cmvef2pas0007qu2o0mvmeuu6","judul":"Ringkasan","konten":"Sistem cek pajak kendaraan (region Bantul, prefix plat KB) terdiri dari 3 bagian:\n\n1. **pajak-kb-api** — backend Next.js (App Router) + Prisma + PostgreSQL + Firebase Cloud Messaging, deploy di Coolify.\n2. **pajak-kb-user** — app Android (Kotlin) untuk pemilik kendaraan.\n3. **pajak-kb-admin** — app Android (Kotlin) untuk admin yang mengisi nominal.\n\nAlur inti: user kirim plat + no WA -> admin input 4 nominal -> user transfer -> upload bukti + konfirmasi WhatsApp -> admin verifikasi.","media":"[]","urut":0,"createdAt":"2026-10-18T23:46:38.677Z","updatedAt":"2026-10-18T23:46:38.677Z"},{"id":"cmveh5hya0003n32oeoow747r","blueprintId":"cmvef2pas0007qu2o0mvmeuu6","judul":"Arsitektur & Alur Data","konten":"**Login**\nUser app pakai Credential Manager (Google Sign-In) -> dapat ID token -> POST /api/auth/google { idToken, fcmToken } -> backend verifikasi via google-auth-library (audience = web client ID) -> upsert User -> balikan JWT HS256 (30 hari) + role.\n\nRole ditentukan whitelist env `ADMIN_EMAILS` — dievaluasi ulang setiap login, bukan sign-up bebas.\n\n**Request pajak**\nUser app: input plat (regex `^KB \\d{1,4} [A-Z]{1,3}$`, prefix KB fixed di UI) + nomor WhatsApp -> POST /api/tax-request { platNomor, whatsapp }.\nBackend: normalisasi plat (`KB1475WQ` -> `KB 1475 WQ`) dan WA (`08xx` -> `62xx`), rate limit 5 request/jam per user, simpan status PENDING, kirim FCM ke semua admin.\n\n**Respon admin**\nAdmin app: GET /api/tax-request?scope=all&status=PENDING -> buka detail -> POST /api/tax-request/{id}/response { nominalPajak, biayaCek, biayaAdmin, biayaKirim }.\nBackend: hanya role ADMIN, hanya kalau status PENDING, simpan 4 nominal + respondedAt + responseExpires (24 jam) + buat record Payment status WAITING_UPLOAD -> push FCM ke user.\n\n**Pembayaran**\nUser app: GET /api/bank-accounts (multi-bank: BCA, Mandiri) -> tampil no rekening + tombol salin -> GET /api/payment/{id}/confirm-link balikan URL wa.me dengan pesan prefilled (plat, request id, total).\nUser upload bukti: POST /api/payment/{id}/upload (multipart, max 8MB, jpeg/png/webp/pdf) -> Payment jadi WAITING_VERIFY -> push ke admin.\nAdmin verifikasi: POST /api/payment/{id}/verify { sendStruk?: true } -> PAID atau STRUK_SENT -> push ke user.","media":"[]","urut":1,"createdAt":"2026-10-18T23:46:39.010Z","updatedAt":"2026-10-18T23:46:39.010Z"},{"id":"cmveh5i210005n32oinae7i5t","blueprintId":"cmvef2pas0007qu2o0mvmeuu6","judul":"Model Data (Prisma)","konten":"```prisma\nmodel User {\n  id, googleSub @unique, email @unique, name?, role (USER|ADMIN),\n  fcmToken?, whatsapp?, createdAt\n}\n\nmodel TaxRequest {\n  id, platNomor, whatsapp, status (PENDING|RESPONDED|CANCELLED|EXPIRED),\n  nominalPajak?, biayaCek?, biayaAdmin?, biayaKirim?,\n  userId, adminId?, payment?,\n  createdAt, respondedAt?, responseExpires?, updatedAt\n}\n\nmodel Payment {\n  id, requestId @unique, status (WAITING_UPLOAD|WAITING_VERIFY|PAID|STRUK_SENT),\n  proofUrl?, verifiedAt?, verifiedBy?\n}\n\nmodel BankAccount {\n  id, bank, number, holder, active, order\n}\n```\n\nTotal tagihan = nominalPajak + biayaCek + biayaAdmin + biayaKirim (dihitung backend, bukan dikirim klien).","media":"[]","urut":2,"createdAt":"2026-10-18T23:46:39.145Z","updatedAt":"2026-10-18T23:46:39.145Z"},{"id":"cmveh5i420007n32ozmielm81","blueprintId":"cmvef2pas0007qu2o0mvmeuu6","judul":"API Endpoints","konten":"| Method | Path | Akses | Fungsi |\n|---|---|---|---|\n| POST | /api/auth/google | publik | Verifikasi Google ID token, upsert user, balikan JWT |\n| GET | /api/me | auth | Profil user |\n| PATCH | /api/me | auth | Update whatsapp / fcmToken |\n| POST | /api/tax-request | auth | Buat request (plat + WA) |\n| GET | /api/tax-request | auth | List request (`?status=&scope=all` khusus admin) |\n| GET | /api/tax-request/{id} | auth | Detail (user: milik sendiri) |\n| POST | /api/tax-request/{id}/response | admin | Input 4 nominal |\n| POST | /api/tax-request/{id}/cancel | auth | Batalkan (hanya PENDING) |\n| GET | /api/bank-accounts | auth | Daftar rekening aktif |\n| GET | /api/payment/{id}/confirm-link | auth | URL wa.me prefilled |\n| POST | /api/payment/{id}/upload | auth | Upload bukti transfer (multipart) |\n| POST | /api/payment/{id}/verify | admin | Verifikasi pembayaran |\n| GET | /api/uploads/{file} | publik | Serve file bukti (blok path traversal) |\n| GET | /api/health | publik | Healthcheck Coolify |","media":"[]","urut":3,"createdAt":"2026-10-18T23:46:39.219Z","updatedAt":"2026-10-18T23:46:39.219Z"},{"id":"cmveh5i630009n32o9ueieph6","blueprintId":"cmvef2pas0007qu2o0mvmeuu6","judul":"Backend — Konfigurasi Env","konten":"```\nDATABASE_URL=postgres://postgres:<PASS>@<container>:5432/postgres?schema=public\nJWT_SECRET=<32+ char random>\nGOOGLE_CLIENT_ID=<web client id>\nADMIN_EMAILS=andy@example.com,admin2@example.com\nADMIN_WA_NUMBER=62812xxxxxxx\nFCM_SERVICE_ACCOUNT_B64=<base64 service account json>\nAPP_BASE_URL=https://...\nUPLOAD_DIR=data/uploads\n```\n\nSetup produksi: Coolify -> New resource dari GitHub `djvpri/pajak-kb-api` -> build pack Nixpacks -> tambah Postgres -> set env -> deploy -> jalankan `npm run db:migrate && npm run db:seed`.\n\nCatatan: prisma harus v5 (`prisma@5` + `@prisma/client@5`); prisma 8 rc tidak punya command `generate` di CLI-nya.","media":"[]","urut":4,"createdAt":"2026-10-18T23:46:39.291Z","updatedAt":"2026-10-18T23:46:39.291Z"},{"id":"cmveh5ibn000bn32ox6h8z1s5","blueprintId":"cmvef2pas0007qu2o0mvmeuu6","judul":"Status & State Machine","konten":"**Request**\n`PENDING` -> (admin isi nominal) -> `RESPONDED`\n`PENDING` -> (user/admin batal) -> `CANCELLED`\n`RESPONDED` -> (lewat responseExpires 24 jam) -> `EXPIRED`\n\n**Payment**\n`WAITING_UPLOAD` -> (user upload bukti) -> `WAITING_VERIFY`\n`WAITING_VERIFY` -> (admin verify) -> `PAID`\n`PAID` -> (admin kirim struk) -> `STRUK_SENT`","media":"[]","urut":5,"createdAt":"2026-10-18T23:46:39.491Z","updatedAt":"2026-10-18T23:46:39.491Z"},{"id":"cmveh5ied000dn32o3tqfenyk","blueprintId":"cmvef2pas0007qu2o0mvmeuu6","judul":"Desain UI (V1 Clean Minimal)","konten":"Dipilih dari 3 variant mockup. Karakter: putih + biru `#0a7cff`, whitespace lebar, tipografi system font, kartu border tipis `#e3e6ea`, radius 10–12px.\n\n**User app — 5 layar**\n1. Login — judul \"Cek Pajak Kendaraan\" + tombol \"Lanjutkan dengan Google\".\n2. Input — plat 3 kotak (KB fixed | angka | kode), field nomor WhatsApp dengan prefix +62 hijau, tombol \"Kirim Permintaan\".\n3. Menunggu — tag status, plat, spinner, request ID, tombol Batalkan.\n4. Rincian — total besar biru, 4 baris (pajak, biaya cek, biaya admin, biaya kirim), tombol **Bayar**.\n5. Pembayaran — total, kartu rekening (bank, no rek + tombol Salin, atas nama), tombol hijau \"Konfirmasi via WhatsApp\", catatan upload bukti.\n\n**Admin app — 4 layar**\n1. Antrian — tab Pending/Selesai/Batal dengan badge, kartu request (plat, nama, no WA, waktu).\n2. Detail — plat highlight, nama, no WA, waktu, tombol Input Nominal / Tolak.\n3. Input Nominal — 4 field rupiah + total auto-sum, tombol Kirim ke User.\n4. Konfirmasi — checkmark, status Selesai, rincian + total, tombol Lihat Antrian.","media":"[]","urut":6,"createdAt":"2026-10-18T23:46:39.589Z","updatedAt":"2026-10-18T23:46:39.589Z"},{"id":"cmveh5igb000fn32ol4ylw658","blueprintId":"cmvef2pas0007qu2o0mvmeuu6","judul":"Langkah Berikutnya","konten":"1. **Backend**: push kode (selesai, 37 file di `djvpri/pajak-kb-api`), set env Coolify, jalankan migration + seed, deploy.\n2. **Google Cloud**: buat OAuth client Android untuk `com.pajakkb.user` dan `com.pajakkb.admin` (package name + SHA-1), web client untuk verifikasi token backend.\n3. **Firebase**: buat project / reuse, download service account JSON -> base64 -> `FCM_SERVICE_ACCOUNT_B64`; `google-services.json` untuk kedua app.\n4. **User app**: Kotlin + Compose, Credential Manager, Retrofit, FCM, 5 layar sesuai desain.\n5. **Admin app**: Kotlin + Compose, role guard (hanya ADMIN boleh masuk), 4 layar.\n6. **Belum dibuat**: cron expire request lewat 24 jam (`scripts/expire.mjs`), rate limit pindah ke Redis kalau multi-instance, storage bukti pindah S3/R2 kalau traffic naik.","media":"[]","urut":7,"createdAt":"2026-10-18T23:46:39.660Z","updatedAt":"2026-10-18T23:46:39.660Z"}],"children":[],"parent":null}